import csv
import random
import math
import numpy
import scipy
import sys
import re
from scipy import optimize
from numpy import savetxt, loadtxt

# Number of inputs
D = 9

M = None
w1 = None
w2 = None

try:
    # Number of hidden layers plus one biased parameter
    M = int(sys.argv[1]) + 1
    # Weight vector from hidden layers to one output
    w2 = numpy.arange(float(M))
    # Weight matrix from input to hidden layers
    w1 = numpy.arange(float(D) * (M - 1)).reshape((M - 1), D)

    # Init w1 and w2
    for e in w2.flat:
        w2.itemset(e,random.random() + random.randint(-10, 10))

    for e in w1.flat:
        w1.itemset(e,random.random() + random.randint(-10, 10))
except ValueError:
    pass

# Biased input parameter
x_0 = 1

# Split data set into training set and test set
def split(filename):
    reader = csv.reader(open(filename, "rU"))
    testWriter = csv.writer(open("testSet.csv", "w"))
    trainingWriter = csv.writer(open("trainingSet.csv", "w"))
    for row in reader:
        #Test data
        if random.random() < 0.25:
            testWriter.writerow(row)
        #Training data
        else:
            trainingWriter.writerow(row)
            
# Read in a cvs file and return and array with all the rows
# and the values cast to floats.
# The rows consist of tuples containing input and output
def parse(filename):
    reader = csv.reader(open(filename, "rU"))
    result = []

    for row in reader:
        # Get the output
        y = float(row[8])
        # Remove the output
        del row[8]
        # Add (input, y) to resulting rows
        result.append(([x_0] + [float(r) for r in row], y))

    return result

# Output function
def y(x, w1, w2):
    sum = 0
    for j in range(M - 1):
        h = 0
        for i in range(D):
            h = h + w1[j,i] * x[i]
        h = math.tanh(h)

        sum = sum + w2[j] * h

    return sum + w2[M - 1]

def geterror(rows):
    def error(w):
        w1, w2 = unpack(w)

        sum = 0
        for row in rows:
            x_n,t_n = row
            sum += (y(x_n, w1, w2) - t_n) ** 2

        return 0.5 * sum

    """
    def error(w):
        w1, w2 = unpack(w)

        sum = 0
        for row in rows:
            x_n, t_n = row
            y_n = y(x_n, w1, w2)
            print "<----------- y_n: ", y_n

            sum += t_n * math.log(y_n) + (1 - t_n) * math.log(1 - y_n)
            return -sum
    """

    return error
    
def pack(w1, w2):
    m = numpy.arange(M + float(D) * (M - 1))

    for i in m.flat:
        if i < M:
            m[i] = w2[i]
        else:
            m[i] = w1.item(i - M)

    return m

def unpack(a):
    w2 = numpy.arange(float(M))
    w1 = numpy.arange((M - 1) * float(D)).reshape(M - 1, D)

    for i in range(len(a)):
        if i < M:
            w2[i] = a[i]
        else:
            w1.itemset(i - M, a[i])

    return w1, w2

def minimize(filename, desc):
    rows = parse(filename)
    error = geterror(rows)

    w = optimize.fmin(error, pack(w1, w2))
    savetxt("result-%s.txt" % desc, w, '%f', ',')

    return w

def compare(filename, w1, w2):
    rows = parse(filename)

    error = 0
    for row in rows:
        x_n, t_n = row
        error += math.fabs((t_n - y(x_n, w1, w2)) / t_n)

    return error / len(rows)

def cod(filename, w1, w2):
    rows = parse(filename)

    y_avg = sum([y_i for inp,y_i in rows]) / float(len(rows))
    #print "y_avg =", y_avg

    ss_tot = sum([(y_i - y_avg) ** 2 for inp,y_i in rows])
    ss_err = sum([(y_i - y(inp, w1, w2)) ** 2 for inp,y_i in rows])

    #print ss_tot, ss_err

    return 1.0 - (ss_err / ss_tot)


if __name__ == '__main__':
    #split("Concrete_Data.csv")

    w = None
    if not M is None:
        desc = "hidden-layers-%d" % M
        print "Minimizing error for " + desc
        w = minimize("trainingSet.csv", desc)
    else:
        m = re.search('\d+', sys.argv[1])
        M = int(m.group(0))
        w = loadtxt(sys.argv[1], delimiter=',')

    print w
    w1, w2 = unpack(w)

    rows = parse("trainingSet.csv")
    error = geterror(rows)

    #print "Init error:", error(pack(w1, w2))
    print "Training error:", error(w)
    print "Training R2:", cod("trainingSet.csv", w1, w2)
    print "Training weighted avg:", compare("trainingSet.csv", w1, w2)

    rows = parse("testSet.csv")
    error = geterror(rows)

    print "Test error:", error(w)
    print "Test R2:", cod("testSet.csv", w1, w2)
    print "Test weighted avg:", compare("testSet.csv", w1, w2)

